package com.odobenus.client.tools;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.stream.Stream;

import org.springframework.boot.context.properties.ConfigurationProperties;

import com.odobenus.client.anno.OdobenusItem;
import com.odobenus.client.processor.AplolloBeanPostProcessor;
import com.odobenus.client.properties.OdobenusProperties;

public class BeanUtils {
	private static final String BASE_DIR="src/main/resources";
	private static String buildLocalFilePath(OdobenusProperties odobenusProperties, String filePath) {
		if("apollo".equals(odobenusProperties.getMode())){
			if(!new File(BASE_DIR+"/apollo").exists()) {
				new File(BASE_DIR+"/apollo").mkdir();
			}if(!new File(BASE_DIR+"/apollo"+"/"+odobenusProperties.getLocalDownDir()).exists()) {
				new File(BASE_DIR+"/apollo"+"/"+odobenusProperties.getLocalDownDir()).mkdir();
			}if(!new File(BASE_DIR+"/apollo"+"/"+odobenusProperties.getLocalDownDir()+"/app").exists()) {
				new File(BASE_DIR+"/apollo"+"/"+odobenusProperties.getLocalDownDir()+"/app").mkdir();
			}if(!new File(BASE_DIR+"/apollo"+"/"+odobenusProperties.getLocalDownDir()+"/app/"+odobenusProperties.getApp()).exists()) {
				new File(BASE_DIR+"/apollo"+"/"+odobenusProperties.getLocalDownDir()+"/app/"+odobenusProperties.getApp()).mkdir();
			}if(!new File(BASE_DIR+"/apollo"+"/"+odobenusProperties.getLocalDownDir()+"/app/"+odobenusProperties.getApp()+"/version").exists()) {
				new File(BASE_DIR+"/apollo"+"/"+odobenusProperties.getLocalDownDir()+"/app/"+odobenusProperties.getApp()+"/version").mkdir();
			}if(!new File(BASE_DIR+"/apollo"+"/"+odobenusProperties.getLocalDownDir()+"/app/"+odobenusProperties.getApp()+"/version"+"/"+odobenusProperties.getVersion()).exists()) {
				new File(BASE_DIR+"/apollo"+"/"+odobenusProperties.getLocalDownDir()+"/app/"+odobenusProperties.getApp()+"/version"+"/"+odobenusProperties.getVersion()).mkdir();
			}if(!new File(BASE_DIR+"/apollo"+"/"+odobenusProperties.getLocalDownDir()+"/app/"+odobenusProperties.getApp()+"/version"+"/"+odobenusProperties.getVersion()+"/"+odobenusProperties.getEnv()).exists()) {
				new File(BASE_DIR+"/apollo"+"/"+odobenusProperties.getLocalDownDir()+"/app/"+odobenusProperties.getApp()+"/version"+"/"+odobenusProperties.getVersion()+"/"+odobenusProperties.getEnv()).mkdir();
			}
			return BASE_DIR.concat("/apollo").concat(
							odobenusProperties.getLocalDownDir()
			        		.concat("/app/")
			        		.concat(odobenusProperties.getApp())
			        		.concat("/version")
			        		.concat("/"+odobenusProperties.getVersion())
			        		.concat("/"+odobenusProperties.getEnv())
			        		.concat("/"+filePath));
		}else {
			if(!new File(BASE_DIR+"/"+odobenusProperties.getLocalDownDir()).exists()) {
				new File(BASE_DIR+"/"+odobenusProperties.getLocalDownDir()).mkdir();
			}if(!new File(BASE_DIR+"/"+odobenusProperties.getLocalDownDir()+"/app").exists()) {
				new File(BASE_DIR+"/"+odobenusProperties.getLocalDownDir()+"/app").mkdir();
			}if(!new File(BASE_DIR+"/"+odobenusProperties.getLocalDownDir()+"/app/"+odobenusProperties.getApp()).exists()) {
				new File(BASE_DIR+"/"+odobenusProperties.getLocalDownDir()+"/app/"+odobenusProperties.getApp()).mkdir();
			}if(!new File(BASE_DIR+"/"+odobenusProperties.getLocalDownDir()+"/app/"+odobenusProperties.getApp()+"/version").exists()) {
				new File(BASE_DIR+"/"+odobenusProperties.getLocalDownDir()+"/app/"+odobenusProperties.getApp()+"/version").mkdir();
			}if(!new File(BASE_DIR+"/"+odobenusProperties.getLocalDownDir()+"/app/"+odobenusProperties.getApp()+"/version"+"/"+odobenusProperties.getVersion()).exists()) {
				new File(BASE_DIR+"/"+odobenusProperties.getLocalDownDir()+"/app/"+odobenusProperties.getApp()+"/version"+"/"+odobenusProperties.getVersion()).mkdir();
			}if(!new File(BASE_DIR+"/"+odobenusProperties.getLocalDownDir()+"/app/"+odobenusProperties.getApp()+"/version"+"/"+odobenusProperties.getVersion()+"/"+odobenusProperties.getEnv()).exists()) {
				new File(BASE_DIR+"/"+odobenusProperties.getLocalDownDir()+"/app/"+odobenusProperties.getApp()+"/version"+"/"+odobenusProperties.getVersion()+"/"+odobenusProperties.getEnv()).mkdir();
			}
			return BASE_DIR.concat(
							odobenusProperties.getLocalDownDir()
			        		.concat("/app/")
			        		.concat(odobenusProperties.getApp())
			        		.concat("/version")
			        		.concat("/"+odobenusProperties.getVersion())
			        		.concat("/"+odobenusProperties.getEnv())
			        		.concat("/"+filePath));
		}
	}
	
	private static String buildRemoteUrl(OdobenusProperties odobenusProperties, String filePath) {
		String url=odobenusProperties.getServerHost()
				.concat("/download?fileName="+filePath)
				.concat("&app="+odobenusProperties.getApp())
				.concat("&version="+odobenusProperties.getVersion())
				.concat("&env="+odobenusProperties.getEnv());
		return url;
	}
	
	public static void downloadFile(OdobenusProperties odobenusProperties, String filePath) {
		String url=buildRemoteUrl(odobenusProperties, filePath);
		try {
			System.out.println("fetch remote file from:"+url);
			HttpTools.getFile(url, buildLocalFilePath(odobenusProperties, filePath));
			System.out.println("fetch file "+filePath+" success!");
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	
	
	public static InputStream downloadFileInputStream(OdobenusProperties odobenusProperties, String filePath) {
		String url=buildRemoteUrl(odobenusProperties, filePath);
		downloadFile(odobenusProperties, filePath);
		return HttpTools.getFileInputStream(url);
	}
	
	public static Properties load(OdobenusProperties odobenusProperties, String filePath) {
		try {
			Properties pro=new Properties();
			File file = new File(buildLocalFilePath(odobenusProperties, filePath));
			InputStream inStream = new FileInputStream(file);
			pro.load(inStream);
			return pro;
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

	
	public static void changeFieldValue(Properties props,Object bean,Field[] declaredFields) {
		if(props!=null) {
			for (Field field : declaredFields) {
				OdobenusItem odobenusItem = field.getAnnotation(OdobenusItem.class);
				if (odobenusItem != null) {
					String[] items=odobenusItem.value().split(":");
					String itemKey=null;
					String itemDefaultVlue=null;
					if(items!=null) {
						if(items.length==1) {
							itemKey=items[0];
						}else if(items.length==2) {
							itemKey=items[0];
							itemDefaultVlue=items[1];
						}
					}
					String configerItemValue = (String) props.get(itemKey);
					if(configerItemValue==null) {
						configerItemValue=itemDefaultVlue;
					}
					try {
						setFieldData(field, bean, configerItemValue);
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			}
		}
	}
	
	public static void changeFieldValueApollo(Properties props,Object bean,Field[] declaredFields) {
		if(props!=null) {
			Map<String, List<Object>> hasValueMap = AplolloBeanPostProcessor.hasValueMap;
			Set propertySet = hasValueMap.entrySet();
			for (Object o : propertySet) {
				Map.Entry entry = (Map.Entry) o;
				((List<Object>)entry.getValue()).stream().forEach(obj->{
					Stream.of(declaredFields).forEach(field->{
						try {
							if(BeanUtils.isSimpleType(field)) {
								ConfigurationProperties configurationProperties = obj.getClass().getAnnotation(ConfigurationProperties.class);
								String prefix = configurationProperties.prefix();
								String key = prefix.concat(".").concat(field.getName());
								Object prop =props.get(key);
								if(entry.getKey().equals(key)&&prop!=null) {
									try {
										System.out.println(key+"-----------"+prop);
										BeanUtils.setFieldData(field, bean, String.valueOf(prop));
									} catch (Exception e) {
										e.printStackTrace();
									}
								}
							}
						} catch (Exception e) {
							e.printStackTrace();
						}
					});
				});
			}
		}
	}

	public static void setFieldData(Field field, Object bean, String data) throws Exception {
		// 注意这里要设置权限为true
		field.setAccessible(true);
		Class<?> type = field.getType();
		if (type.equals(String.class)) {
			field.set(bean, data);
		} else if (type.equals(Integer.class)) {
			field.set(bean, Integer.valueOf(data));
		} else if (type.equals(Long.class)) {
			field.set(bean, Long.valueOf(data));
		} else if (type.equals(Double.class)) {
			field.set(bean, Double.valueOf(data));
		} else if (type.equals(Short.class)) {
			field.set(bean, Short.valueOf(data));
		} else if (type.equals(Byte.class)) {
			field.set(bean, Byte.valueOf(data));
		} else if (type.equals(Boolean.class)) {
			field.set(bean, Boolean.valueOf(data));
		} else if (type.equals(Date.class)) {
			field.set(bean, new Date(Long.valueOf(data)));
		}
	}
	
	public static boolean isSimpleType(Field field) throws Exception {
		  boolean isFinal = Modifier.isFinal(field.getModifiers());
		if(isFinal) {
			return false;
		}
		Class<?> type=field.getType();
		// 注意这里要设置权限为true
		if (type.equals(String.class)) {
			return true;
		} else if (type.equals(Integer.class)) {
			return true;
		} else if (type.equals(Long.class)) {
			return true;
		} else if (type.equals(Double.class)) {
			return true;
		} else if (type.equals(Short.class)) {
			return true;
		} else if (type.equals(Byte.class)) {
			return true;
		} else if (type.equals(Boolean.class)) {
			return true;
		} else if (type.equals(Date.class)) {
			return true;
		}
		return false;
	}


	public static InputStream loadLocalFileInputStream(OdobenusProperties odobenusProperties, String filePath) throws FileNotFoundException {
		String localPath = buildLocalFilePath(odobenusProperties, filePath);
		return new FileInputStream(localPath);
	}
}
